#include "WorldSector.h"

Vector3 getNormal(float ax, float ay, float az, float bx, float by, float bz, float cx, float cy, float cz) {
	return Vector3(bx-ax, by-ay, bz-az).cross(Vector3(cx-ax, cy-ay, cz-az)).calcNormalVect();
}

WorldSector::WorldSector(int xpos, int zpos) : xpos(xpos), zpos(zpos) {
	scale = 2.0f;

	//Create heightmap
	for (int x=0;x<size;x++) {
		for (int z=0;z<size;z++) {
			heightmap[x][z] = (sin((float)x) + cos((float)z)) / 2;
		}
	}

	//Calculate Normals
	for (int x=0;x<size;x++) {
		float thisx = x*scale+xpos;
		for (int z=0;z<size;z++) {
			vector<Vector3> calcNormals;
			float thisz = z*scale+zpos;
			if (x!=size-1) {		// If x is not at the rightmost edge
				if (z!=size-1) {		// If z is not at the rightmost edge
					//delta
					calcNormals.push_back(getNormal(thisx,				heightmap[x][z],	thisz,
													thisx,				heightmap[x][z+1],	(z+1)*scale+zpos,
													(x+1)*scale+zpos,	heightmap[x+1][z+1],(z+1)*scale+zpos));
					//epsilon
					calcNormals.push_back(getNormal(thisx,				heightmap[x][z],	thisz,
													(x+1)*scale+zpos,	heightmap[x+1][z+1],(z+1)*scale+zpos,
													(x+1)*scale+zpos,	heightmap[x+1][z],	thisz));
				}
				if (z!=0) {				// If z is not at the leftmost edge
					//zeta
					calcNormals.push_back(getNormal(thisx,				heightmap[x][z],	thisz,
													(x+1)*scale+zpos,	heightmap[x+1][z],	thisz,
													thisx,				heightmap[x][z-1],	(z-1)*scale+zpos));
				}
			}
			if (x!=0) {				// If x is not at the leftmost edge
				if (z!=size-1) {		// If z is not at the rightmost edge
					//gamma
					calcNormals.push_back(getNormal(thisx,				heightmap[x][z],	thisz,
													thisx,				heightmap[x][z-1],	(z-1)*scale+zpos,
													(x-1)*scale+zpos,	heightmap[x-1][z],	thisz));
				}
				if (z!=0) {				// If z is not at the leftmost edge
					//alpha
					calcNormals.push_back(getNormal(thisx,				heightmap[x][z],	thisz,
													thisx,				heightmap[x][z-1],	(z-1)*scale+zpos,
													(x-1)*scale+zpos,	heightmap[x-1][z-1],(z-1)*scale+zpos));
					//beta
					calcNormals.push_back(getNormal(thisx,				heightmap[x][z],	thisz,
													(x-1)*scale+zpos,	heightmap[x-1][z-1],(z-1)*scale+zpos,
													(x-1)*scale+zpos,	heightmap[x-1][z],	thisz));
				}
			}

			Vector3 finalNormal = Vector3(0, 0, 0);
			for (unsigned int i=0;i<calcNormals.size();i++) {
				finalNormal = finalNormal.add(calcNormals[i]);
			}

			normals[x][z] = finalNormal.calcNormalVect();
		}
	}

	//Create grassfields
	for (int z=0;z<size-1;z++) {
		for (int x=0;x<size-1;x++) {
			Vector3 patchNormals[4] = {	normals[x][z],
										normals[x+1][z],
										normals[x+1][z+1],
										normals[x][z+1]};

			Point3D points[4]= {{	 x*scale+xpos, heightmap[x][z],		z*scale+zpos},
								{(x+1)*scale+xpos, heightmap[x+1][z],	z*scale+zpos},
								{(x+1)*scale+xpos, heightmap[x+1][z+1], (z+1)*scale+zpos},
								{	 x*scale+xpos, heightmap[x][z+1],	(z+1)*scale+zpos}};
			GrassPatch* patch = new GrassPatch(points, 75, 1.0f, patchNormals);
			patch->setPosition(x*scale+xpos, heightmap[x][z], z*scale+zpos);
			patches.push_back(patch);
		}
	}
}

void setColorMaterial()
{
	GLfloat mat_ambient[] = {0.5f, 0.5f, 0.5f, 1.0f};
	GLfloat mat_diffuse[] = {0.8f, 0.8f, 0.8f, 1.0f};
	GLfloat mat_specular[] = {0.8f, 0.8f, 0.8f, 1.0f};
	GLfloat mat_shininess[] = {0.5f};
	glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient);
	glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse);
	glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular);
	glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess);
}

void WorldSector::update(float delta) {
	for (unsigned int i=0;i<patches.size()-1;i++) {
		patches[i]->update(delta);
	}
}

void WorldSector::draw() {
	setColorMaterial();
	for (unsigned int i=0;i<patches.size()-1;i++) {
		patches[i]->draw();
	}
}